package com.fansl.allround.auth.provider.social;

import cn.hutool.core.util.StrUtil;
import com.fansl.allround.auth.dto.BindSocialUser;
import com.fansl.allround.auth.dto.ExistSocialUser;
import com.fansl.allround.auth.provider.social.qq.api.QQApi;
import com.fansl.allround.auth.provider.social.qq.bean.QQOpenId;
import com.fansl.allround.auth.provider.social.qq.bean.QQToken;
import com.fansl.allround.auth.provider.social.qq.bean.QQUserInfo;
import com.fansl.allround.auth.provider.social.weibo.api.WeiboApi;
import com.fansl.allround.auth.provider.social.weibo.bean.WeiboToken;
import com.fansl.allround.auth.provider.social.weibo.bean.WeiboUserInfo;
import com.fansl.allround.auth.provider.sys.openid.SysUserOpenIdAuthenticationToken;
import com.fansl.allround.common.core.bean.SocialInfo;
import com.fansl.allround.common.core.bean.UserInfo;
import com.fansl.allround.common.core.constant.SecurityConstants;
import com.fansl.allround.common.core.constant.enums.*;
import com.fansl.allround.common.core.exception.CheckedException;
import com.fansl.allround.upms.api.dto.BindSocialInfoDTO;
import com.fansl.allround.upms.api.dto.SysSocialUserDTO;
import com.fansl.allround.upms.api.dto.UnbindSocialInfoDTO;
import com.fansl.allround.upms.api.entity.SysUser;
import com.fansl.allround.upms.api.feign.RemoteUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author fansl
 * @Description: 注册个人中心用户
 * @date 2019/11/6 10:26
 */
@Service
@Slf4j
public class SysSocialUserSignUp implements SocialUserSignUp {
    @Resource
    private QQApi qqApi;
    @Resource
    private WeiboApi weiboApi;
    @Resource
    private RemoteUserService remoteUserService;
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 注册用户
     *
     * @param authentication
     * @return
     */
    @Override
    public UserInfo registerSocialUser(Authentication authentication) {
        SysUserOpenIdAuthenticationToken token = (SysUserOpenIdAuthenticationToken) authentication;
        OauthTypeEnum oauthType = OauthTypeEnum.getByCode(Integer.parseInt(token.getProviderId()));
        String accessToken = token.getAccessToken();
        String openId = (String) token.getPrincipal();
        String username = token.getUsername();
        String password = token.getPassword();
        //绑定已有用户
        if (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(password)) {
            UserInfo userInfo = remoteUserService.info(username, SecurityConstants.FROM_IN);
            SysUser sysUser = (SysUser) userInfo.getUser();
            //匹配密码
            if (!sysUser.getPassword().equals(password)) {
                throw new AuthenticationServiceException("用户名或密码错误");
            }
            //绑定信息
            BindSocialInfoDTO bindSocialInfo = new BindSocialInfoDTO();
            bindSocialInfo.setUserId(sysUser.getUserId());
            bindSocialInfo.setOauthType(oauthType);
            bindSocialInfo.setOpenId(openId);
            remoteUserService.bindSocialInfo(bindSocialInfo, SecurityConstants.FROM_IN);
            return userInfo;
        }
        //注册用户
        switch (oauthType) {
            case QQ: {
                QQUserInfo qqUserInfo = qqApi.getUserInfo(accessToken, openId);
                if (qqUserInfo != null) {
                    //新建用户
                    SysUser user = new SysUser();
                    user.setNickname(qqUserInfo.getNickname());
                    user.setAvatar(qqUserInfo.getFigureurl_qq_1());
                    user.setSex(qqUserInfo.getGender()
                            .equals("男") ? SexEnum.MALE.getType() : SexEnum.FEMALE.getType());
                    user.setLockFlag(String.valueOf(UserStatusEnum.NORMAL.getCode()));
                    user.setQqOpenid(openId);
                    //组装参数
                    SysSocialUserDTO socialUser = new SysSocialUserDTO();
                    socialUser.setSysUser(user);
                    socialUser.setOauthType(oauthType);
                    socialUser.setOpenId(openId);
                    return remoteUserService.createSocialUser(socialUser, SecurityConstants.FROM_IN);
                }
                break;
            }
            case WEIBO:
                WeiboUserInfo weiboUserInfo = weiboApi.getUserInfo(accessToken, openId);
                if (weiboUserInfo != null) {
                    //新建用户
                    SysUser user = new SysUser();
                    user.setNickname(weiboUserInfo.getScreen_name());
                    user.setAvatar(weiboUserInfo.getAvatar_large());
                    user.setSex(weiboUserInfo.getGender()
                            .equals("m") ? SexEnum.MALE.getType() : (weiboUserInfo.getGender().equals("f") ? SexEnum.FEMALE.getType() : SexEnum.UNKNOWN.getType()));
                    user.setLockFlag(String.valueOf(UserStatusEnum.NORMAL.getCode()));
                    user.setWeiboOpenid(openId);
                    //组装参数
                    SysSocialUserDTO socialUser = new SysSocialUserDTO();
                    socialUser.setSysUser(user);
                    socialUser.setOauthType(oauthType);
                    socialUser.setOpenId(openId);
                    return remoteUserService.createSocialUser(socialUser, SecurityConstants.FROM_IN);
                }
                break;
            default:
                throw new AuthenticationServiceException("oauth type not support");
        }
        return null;
    }

    /**
     * 检查用户是否存在
     *
     * @param oauthType
     * @param code
     * @return
     */
    @Override
    public ExistSocialUser existUser(OauthTypeEnum oauthType, String code) {
        SocialLoginInfo socialLoginInfo = new SocialLoginInfo();
        UserInfo userInfo = null;
        String socialLoginOpenIdKey =
                String.format(SecurityConstants.SOCIAL_LOGIN_OPENID_KEY, ServiceTypeEnum.BACKEND.name(), oauthType.name(), code);
        boolean haveSocialInfo = redisTemplate.hasKey(socialLoginOpenIdKey);
        if (haveSocialInfo) {
            socialLoginInfo = (SocialLoginInfo) redisTemplate.opsForValue().get(socialLoginOpenIdKey);
            userInfo = remoteUserService.social(socialLoginInfo.getOpenId(), oauthType, SecurityConstants.FROM_IN);
        } else {
            switch (oauthType) {
                case PASSWORD:
                    break;
                case PHONE:
                    break;
                case WECHAT:
                    break;
                case QQ: {
                    QQToken qqToken = qqApi.getAccessToken(code);
                    QQOpenId qqOpenId = qqApi.getOpenId(qqToken.getAccessToken());
                    userInfo = remoteUserService.social(qqOpenId.getOpenId(), oauthType, SecurityConstants.FROM_IN);
                    //把社交登录的信息保存在redis
                    socialLoginInfo.setCode(code)
                            .setAccessToken(qqToken.getAccessToken())
                            .setOpenId(qqOpenId.getOpenId())
                            .setOauthType(oauthType);
                    redisTemplate.opsForValue().set(socialLoginOpenIdKey, socialLoginInfo, qqToken.getExpiresIn(), TimeUnit.SECONDS);
                    break;
                }
                case WEIBO: {
                    WeiboToken weiboToken = weiboApi.getAccessToken(code);
                    userInfo = remoteUserService.social(weiboToken.getUid(), oauthType, SecurityConstants.FROM_IN);
                    //把社交登录的信息保存在redis
                    socialLoginInfo.setCode(code)
                            .setAccessToken(weiboToken.getAccessToken())
                            .setOpenId(weiboToken.getUid())
                            .setOauthType(oauthType);
                    redisTemplate.opsForValue().set(socialLoginOpenIdKey, socialLoginInfo, weiboToken.getExpiresIn(), TimeUnit.SECONDS);
                    break;
                }
                case EMAIL:
                    break;
                default:
            }
        }
        ExistSocialUser existSocialUser = new ExistSocialUser();
        existSocialUser.setExist(false);
        existSocialUser.setExist(Objects.nonNull(userInfo.getUser()));

        if (!existSocialUser.getExist()) {
            //不存在获取社交用户信息
            existSocialUser.setSocialName(getSocialInfo(oauthType, socialLoginInfo));
        }

        return existSocialUser;
    }

    /**
     * 绑定社交用户
     *
     * @param param
     * @return
     */
    @Override
    public Boolean bindSocialUser(BindSocialUser param) {
        //查找当前手机号是否已被绑定
        UserInfo userInfo = remoteUserService.mobile(param.getMobile(), SecurityConstants.FROM_IN);

        if (!userInfo.isExistUser()) {
            throw new CheckedException("当前手机号用户不存在");
        }
        List<SocialInfo> socialInfos = userInfo.getSocialInfoList();
        for (SocialInfo socialInfo : socialInfos) {
            boolean isBind = socialInfo.getOauthType().equals(param.getOauthType())
                    && UserOauthStatusEnum.BIND.equals(socialInfo.getOauthStatus());
            if (isBind) {
                log.warn("手机号{}已经被绑定了", param.getMobile());
                throw new CheckedException("当前手机号已经被绑定了");
            }
        }
        String socialLoginOpenIdKey =
                String.format(SecurityConstants.SOCIAL_LOGIN_OPENID_KEY, param.getServiceType().name(), param.getOauthType().name(), param.getCode());
        Object socialLoginInfoObj = redisTemplate.opsForValue().get(socialLoginOpenIdKey);
        if (Objects.isNull(socialLoginInfoObj)) {
            throw new CheckedException("请重新登录");
        }
        SocialLoginInfo socialLoginInfo = (SocialLoginInfo) socialLoginInfoObj;
        //绑定用户信息
        BindSocialInfoDTO bindSocialInfo = new BindSocialInfoDTO();
        bindSocialInfo.setOauthType(param.getOauthType());
        bindSocialInfo.setOpenId(socialLoginInfo.getOpenId());
        bindSocialInfo.setUserId(userInfo.getUserId());
        return remoteUserService.bindSocialInfo(bindSocialInfo, SecurityConstants.FROM_IN);
    }

    /**
     * 绑定第三方平台应用
     *
     * @param userId  用户ID
     * @param oauthId 三方平台类型
     * @param code    三方平台code
     * @return true/false
     */
    @Override
    public Boolean bindSocialUser(Integer userId, Integer oauthId, String code) {
        OauthTypeEnum oauthType = OauthTypeEnum.getByCode(oauthId);
        String openId = null;
        switch (oauthType) {
            case WECHAT:
                break;
            case WEIBO:
                WeiboToken weiboToken = weiboApi.getAccessToken(code);
                openId = weiboToken.getUid();
                break;
            case QQ:
                break;
            default:
                break;
        }
        if (StrUtil.isBlank(openId)) {
            return false;
        }
        BindSocialInfoDTO bindSocialInfo = new BindSocialInfoDTO();
        bindSocialInfo.setOauthType(oauthType);
        bindSocialInfo.setOpenId(openId);
        bindSocialInfo.setUserId(userId);
        return remoteUserService.bindSocialInfo(bindSocialInfo, SecurityConstants.FROM_IN);
    }

    /**
     * 解绑第三方平台应用
     *
     * @param userId  用户ID
     * @param oauthId 三方平台类型
     * @return true/false
     */
    @Override
    public Boolean unbindSocialUser(Integer userId, Integer oauthId) {
        UnbindSocialInfoDTO param = new UnbindSocialInfoDTO();
        param.setUserId(userId);
        param.setOauthType(OauthTypeEnum.getByCode(oauthId));
        return remoteUserService.unbindSocialInfo(param, SecurityConstants.FROM_IN);
    }


    public UserInfo getUserByMobile(String mobile) {
        return remoteUserService.mobile(mobile, SecurityConstants.FROM_IN);
    }

    private String getSocialInfo(OauthTypeEnum oauthType, SocialLoginInfo socialLoginInfo) {
        switch (oauthType) {
            case WEIBO:
                return weiboApi.getUserInfo(socialLoginInfo.getAccessToken(), socialLoginInfo.getOpenId()).getName();
            case QQ:
                return qqApi.getUserInfo(socialLoginInfo.getAccessToken(), socialLoginInfo.getOpenId()).getNickname();
            default:
                return "";
        }
    }
}
